package cn.kgc.websocket;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import cn.kgc.base.SystemConstant;
import cn.kgc.entity.ChatMsg;
import cn.kgc.entity.DataContent;
import cn.kgc.utils.RabbitConnectUtil;
import com.rabbitmq.client.Connection;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

import java.util.Date;
import java.util.List;

/**
 * @author YC
 */
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    /**
     * 读取到消息
     * 业务逻辑：
     * 根据不同消息类型执行不同逻辑（数据库设计：消息类型0表示建立连接，1表示文本，2表示图片等等）
     * 无论哪种发送消息的方式，都是将信息数据封装保存到数据库中（表情、图片、语言、视频等文件都是通过minIO等组件封装的是文件的地址）
     *
     * @param ctx   上下文
     * @param frame 消息
     * @throws Exception 抛出异常
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception {
        // 获得客户端给服务端发送的 信息 （JSON格式字符串）， 将JSON字符串转换成DataContent
        String jsonStr = frame.text();
        DataContent dataContent = JSONUtil.toBean(jsonStr, DataContent.class);
        ChatMsg chatMsg = dataContent.getChatMsg();
        // 获得 消息发送者ID 和 消息接受者的类型 和 接收者ID 和 当前用户与服务端连接的channel通道
        String senderId = chatMsg.getSenderId();
        Integer msgType = chatMsg.getMsgType();
        String receiverId = chatMsg.getReceiverId();
        Channel channel = ctx.channel();
        // 判断消息类型
        if (msgType.equals(SystemConstant.CHAT_MSG_TYPE_CONNECT)) {
            // 添加用户ID对应的所有通道 、添加channelID与userID对应关系
            UserChannelSession.putMultiChannels(senderId, channel);
            UserChannelSession.putUserChannelId(channel.id().asLongText(), senderId);
        } else if (msgType.equals(SystemConstant.CHAT_MSG_TYPE_TEXT)) {
            // 设置消息的发送时间，设置封装体的发送时间（字符串类型）
            chatMsg.setChatTime(new Date());
            dataContent.setChatTime(DateUtil.now());
            List<Channel> receiverChannels = UserChannelSession.getMultiChannels(receiverId);
            if (CollUtil.isEmpty(receiverChannels)) {
                // 如果接收者的通道为空，则标记isReceiverOnline非数据库字段为false，放入消息队列后由消费者保存到数据库
                chatMsg.setReceiverOnline(false);
            } else {
                // 如果接收者的通道不为空，则标记isReceiverOnline非数据库字段为true
                chatMsg.setReceiverOnline(true);
                // 接收者在线，给所有的channel发送消息
                for (Channel receiverChannel : receiverChannels) {
                    // 通过channel将封装体dataContent发送给接收者
                    receiverChannel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(dataContent)));
                }
            }
        } else if (msgType.equals(SystemConstant.CHAT_MSG_TYPE_IMAGE)
                || msgType.equals(SystemConstant.CHAT_MSG_TYPE_VIDEO)) {
            // 图片、视频

        } else if (msgType.equals(SystemConstant.CHAT_MSG_TYPE_AUDIO)) {
            // 音频是默认未读的
            chatMsg.setIsRead(SystemConstant.MSG_IS_NOT_READ);
        } else {

        }
        // 如果用户是多端登录，用户在一端发送的消息，在其他端也应该能看到自己发送出的消息
        List<Channel> senderChannels = UserChannelSession.getMultiChannels(senderId);
        // 从发送者的所有通道中删除当前通道
        senderChannels.removeIf(temp -> temp.id().asLongText().equals(channel.id().asLongText()));
        // 如果发送者的所有通道不为空，则给所有通道发送消息
        if (CollUtil.isNotEmpty(senderChannels)) {
            for (Channel tempChannel : senderChannels) {
                tempChannel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(dataContent)));
            }
        }
        // 无论接收者是否在线，都将信息数据保存到数据库（使用RabbitMQ原生API使用）
        sendChatMessage(chatMsg);
    }

    /**
     * 使用RabbitMQ原生API，将消息发送到消息队列
     *
     * @param chatMsg 聊天消息
     * @throws Exception 抛出异常
     */
    private static void sendChatMessage(ChatMsg chatMsg) throws Exception {
        Connection connection = null;
        com.rabbitmq.client.Channel rabbitChannel = null;
        try {
            connection = RabbitConnectUtil.getConnection();
            rabbitChannel = connection.createChannel();
            // 将消息发送到消息队列
            rabbitChannel.basicPublish("chatExchange", "chat", null,
                    JSONUtil.toJsonStr(chatMsg).getBytes());
            UserChannelSession.print("读取到消息");
        } finally {
            if (rabbitChannel != null) {
                // 关闭通道
                rabbitChannel.close();
            }
            if (connection != null) {
                // 归还连接
                RabbitConnectUtil.returnConnection(connection);
            }
        }
    }

    /**
     * 用户手动关闭连接，从用户ID对应的所有通道中删除关闭的通道
     *
     * @param ctx 上下文
     * @throws Exception 抛出异常
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        // 获得当前关闭的通道ID
        String channelId = ctx.channel().id().asLongText();
        // 从用户ID对应所有通道中删除关闭的通道
        UserChannelSession.removeUselessChannel(channelId);
        UserChannelSession.print("用户关闭连接");
    }

}